﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Myu.EventBus.Core
{
    /// <summary>
    /// 事件总线
    /// </summary>
    public class EventBus
    {

        private static EventBus _eventBus = null;

        /// <summary>
        /// 存储事件句柄
        /// </summary>
        private static Dictionary<Type, List<object>> _eventHands = new Dictionary<Type, List<object>>();

        /// <summary>
        /// 锁
        /// </summary>
        public static object _syncObject = new object();


        //为了保证事件总线入口唯一性，使用单例
        public static EventBus Instance {
            get {
                return _eventBus ?? (_eventBus = new EventBus());
            }
        }

        /// <summary>
        /// 初始化事件总线
        /// </summary>
        /// <returns></returns>
        public static EventBus InstanceForXml()
        {
            if (_eventBus == null)
            {
                XElement root = XElement.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "EventBusConfig.xml"));

                foreach (var evt in root.Elements("Event"))
                {
                    List<object> handlers = new List<object>();
                    Type publishEventType = Type.GetType(evt.Element("PublishEvent").Value);
                    foreach (var sub in evt.Elements("SubscribedEvents"))
                    {
                        foreach (var concreteEvt in sub.Elements("SubscribedEvent"))
                        {
                            var type = Type.GetType(concreteEvt.Value);
                            handlers.Add(System.Activator.CreateInstance(type));
                        }
                        EventBus._eventHands.Add(publishEventType, handlers);
                    }
                }
                _eventBus = new EventBus();
            }
            return _eventBus;
        }

        /// <summary>
        /// 订阅事件 因为处理同一个字典，非线程安全。所以要加锁
        /// </summary>
        /// <typeparam name="TEvent"></typeparam>
        /// <param name="eventHandler"></param>
        public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler) where TEvent:IEvent
        {
            //同步锁
            lock (_syncObject)
            {
                var eventType = typeof(TEvent);

                if (_eventHands.ContainsKey(eventType))
                {
                    var handers = _eventHands[eventType];
                    if (handers != null)
                    {
                        handers.Add(eventHandler);
                    }
                    else
                    {
                        handers = new List<object>() { eventHandler };

                    }
                }
                else
                {
                    _eventHands.Add(eventType, new List<object>() { eventHandler });
                }
            }
        }


        /// <summary>
        /// 发布事件
        /// </summary>
        /// <typeparam name="TEvent"></typeparam>
        /// <param name="tEvent"></param>
        /// <param name="callback"></param>
        public void Publish<TEvent>(TEvent tEvent, Action<TEvent, bool, Exception> callback) where TEvent : IEvent
        {
            var type = typeof(TEvent);

            if (_eventHands.ContainsKey(type) && _eventHands[type] != null && _eventHands[type].Count > 0)
            {
                try
                {
                    _eventHands[type].ForEach(hand =>
                    {
                        var hander = hand as IEventHandler<TEvent>;
                        hander.Handle(tEvent);
                        callback(tEvent, true, null);
                    });
                }
                catch (Exception)
                {
                    callback(tEvent, false, null);
                }
            }
            else
            {
                callback(tEvent, false, null);
            }

        }


    }
}
